package slice_learn

import "fmt"

//todo 切片
/**

简单介绍：
	切片slice的底层数据是数组,slice是对数组的封装,它描述一个数组的片段。
	两者都可以通过下标来访问单个元素。 数组是定长的,长度定义好之后,不能再更改。而切片则非常灵活,它可以动态地扩容。
	切片的类型和长度无关。 数组就是一片连续的内存,slice 实际上是一个结构体,包含三个字段：长度、容量、底层数组。切片的数据结构如下图：


	// runtime/slice.go
	type slice struct {
		array unsafe.Pointer // 元素指针,指向一个数组的指针
		len   int // 当前切片的长度
		cap   int // 当前切片的容量
	}



概述
	切片是围绕动态数组概念构建的。但是切片本身并不是动态数组，而是对底层数组的抽象。
	按需自动改变大小
	与数组相比，切片的长度可以在运行时修改
	切片不存具体的值，切片是一个引用类型
	相当于java中的ArrayList



	因为数组的长度是固定的并且数组长度属于类型的一部分，所以数组有很多的局限性。 例如
	func arraySum(x [3]int) int{
		sum := 0
		for _, v := range x{
			sum = sum + v
		}
		return sum
	}
	这个求和函数只能接受[3]int类型，其他的都不支持


声明切片类型的基本语法

var name []T
name:表示变量名 T:表示切片中的元素类型,


切片的长度和容量

切片拥有自己的长度和容量，我们可以通过使用内置的len()函数求长度，使用内置的cap()函数求切片的容量。



基于数组定义切片
	由于切片的底层就是一个数组，所以我们可以基于数组定义切片。


*/

func aaaaa() {
	// 声明切片类型
	var a []string              //声明一个字符串切片
	var b = []int{}             //声明一个整型切片并初始化
	var c = []bool{false, true} //声明一个布尔切片并初始化
	var d = []bool{false, true} //声明一个布尔切片并初始化
	fmt.Println(a)              //[]
	fmt.Println(b)              //[]
	fmt.Println(c)              //[false true]
	fmt.Println(a == nil)       //true
	fmt.Println(b == nil)       //false
	fmt.Println(c == nil)       //false
	// fmt.Println(c == d)   //切片是引用类型，不支持直接比较，只能和nil比较

}

func fff() {
	// 基于数组定义切片
	a := [5]int{55, 56, 57, 58, 59}
	b := a[1:4]                     //基于数组a创建切片，包括元素a[1],a[2],a[3]
	fmt.Println(b)                  //[56 57 58]
	fmt.Printf("type of b:%T\n", b) //type of b:[]int

	c := a[1:]                      //[56 57 58 59]
	d := a[:4]                      //[55 56 57 58]
	e := a[:]                       //[55 56 57 58 59]
	fmt.Println(c)                  //[56 57 58]
	fmt.Printf("type of c:%T\n", c) //type of b:[]int
	fmt.Println(d)                  //[56 57 58]
	fmt.Printf("type of d:%T\n", d) //type of b:[]int
	fmt.Println(e)                  //[56 57 58]
	fmt.Printf("type of e:%T\n", e) //type of b:[]int

}

//todo 切片再切片

func eee() {
	//切片再切片
	a := [...]string{"北京", "上海", "广州", "深圳", "成都", "重庆"}
	fmt.Printf("a:%v type:%T len:%d  cap:%d\n", a, a, len(a), cap(a))
	b := a[1:3]
	fmt.Printf("b:%v type:%T len:%d  cap:%d\n", b, b, len(b), cap(b))
	c := b[1:5]
	fmt.Printf("c:%v type:%T len:%d  cap:%d\n", c, c, len(c), cap(c))
}

//todo 使用make构造切片
//我们上面都是基于数组来创建的切片,如果需要动态的创建一个切片,我们就需要使用内置的make()函数，
//格式如下
//
//make([]T, size, cap)
//    T:切片的元素类型
//    size:切片中元素的数量
//    cap:切片的容量
//func main() {
//	a := make([]int, 2, 10)
//	fmt.Println(a)      //[0 0]
//	fmt.Println(len(a)) //2
//	fmt.Println(cap(a)) //10
//}

/**
切片的本质

切片的本质就是对底层数组的封装，它包含了三个信息：底层数组的指针、切片的长度（len）和切片的容量（cap）。 举个例子，现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}，切片s1 := a[:5]，相应示意图如下。

*/
